﻿// -----------------------------------------------------------------------
// <copyright file="MvcApplicationErrorHandler.cs">
// Copyright (c) 2013-2013 Andrey Veselov. All rights reserved.
// License:  Microsoft Public License (MS-PL)
// Contacts: http://andrey.moveax.com  andrey@moveax.com
// </copyright>
// -----------------------------------------------------------------------

namespace Moveax.Mvc.ErrorHandler
{
    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;

    public class MvcApplicationErrorHandler
    {
        private readonly HttpApplication _application;
        private readonly Exception _exception;
        private readonly string _controllerName;

        public bool EnableHttpReturnCodes { get; set; }

        public bool PassThroughHttp401 { get; set; }

        public MvcApplicationErrorHandler(HttpApplication application, Exception exception)
            : this(application, exception, "Error")
        {
        }

        public MvcApplicationErrorHandler(HttpApplication application, Exception exception, string controllerName)
        {
            if (string.IsNullOrEmpty(controllerName))
                throw new ArgumentNullException("controllerName");

            this._application = application;
            this._exception = exception;
            this._controllerName = controllerName;

            this.EnableHttpReturnCodes = true;
            this.PassThroughHttp401 = true;
        }

        public void Execute()
        {
            // Clear current error code on server
            this._application.Response.Clear();
            this._application.Server.ClearError();

            // Get error code
            var httpErrorCode = new HttpException(null, this._exception).GetHttpCode();

            if ((httpErrorCode == 401) && this.PassThroughHttp401) {
                this._application.Response.StatusCode = httpErrorCode;
                return;
            }

            // Build ErrorController
            string actionName = (httpErrorCode > 0) ?
                string.Format("Http{0}", httpErrorCode) :
                "Index";

            var errorDescription = new ErrorDescription(
                httpErrorCode, this._exception, this._application.Request);

            var routeData = new RouteData();
            routeData.Values.Add("controller", this._controllerName);
            routeData.Values.Add("action", actionName);
            routeData.Values.Add("errorDescription", errorDescription);

            var wrapper = new HttpContextWrapper(this._application.Context);
            var requestContext = new RequestContext(wrapper, routeData);

            var controllersFactory = ControllerBuilder.Current.GetControllerFactory();
            var controller = controllersFactory.CreateController(requestContext, this._controllerName);

            // Build error page
            controller.Execute(requestContext);

            if ((this.EnableHttpReturnCodes) && (httpErrorCode > 0))
                this._application.Response.StatusCode = httpErrorCode;

            this._application.Response.TrySkipIisCustomErrors = true;
        }
    }
}